package drds.global_transaction.global_transaction_manager.api;

import drds.common.ShouldNeverHappenException;
import drds.global_transaction.api.api.GlobalTransactionStatus;
import drds.global_transaction.api.exception.GlobalTransactionException;
import drds.global_transaction.api.global_transaction_id_context.GlobalTransactionIdContexts;
import drds.global_transaction.global_transaction_manager.GlobalTransactionManager;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GlobalTransactionImpl implements GlobalTransaction
{

	private static final int default_global_transaction_timeout = 60 * 000;

	private static final String default_global_transaction_name = "default";

	private drds.global_transaction.api.api.GlobalTransactionManager globalTransactionManager;

	@Setter
	@Getter
	private String globalTransactionIdWithAddressAndPort;
	@Setter
	@Getter
	private GlobalTransactionStatus globalTransactionStatus = GlobalTransactionStatus.unknown;
	@Setter
	@Getter
	private GlobalTransactionRole globalTransactionRole = GlobalTransactionRole.global_transaction_starter;

	/**
	 * global_transaction_starter use this
	 */
	public GlobalTransactionImpl()
	{
		this(null);
	}

	/**
	 * participant use this
	 */
	public GlobalTransactionImpl(@NonNull String globalTransactionIdWithAddressAndPort)
	{
		this.globalTransactionManager = GlobalTransactionManager.get();
		this.globalTransactionIdWithAddressAndPort = globalTransactionIdWithAddressAndPort;
		if (globalTransactionIdWithAddressAndPort != null)
		{
			globalTransactionStatus = GlobalTransactionStatus.begin;
			globalTransactionRole = GlobalTransactionRole.participant;
		}
	}

	@Override
	public void begin() throws GlobalTransactionException
	{
		begin(default_global_transaction_timeout);
	}

	@Override
	public void begin(int timeout) throws GlobalTransactionException
	{
		begin(timeout, default_global_transaction_name);
	}

	@Override
	public void begin(int timeout, String globalTransactionName) throws GlobalTransactionException
	{
		/**
		 * global_transaction_starter
		 */
		if (globalTransactionIdWithAddressAndPort == null && globalTransactionRole == GlobalTransactionRole.global_transaction_starter)
		{
			globalTransactionIdWithAddressAndPort = globalTransactionManager.globalTransactionBegin(null, null, globalTransactionName, timeout);
			globalTransactionStatus = GlobalTransactionStatus.begin;
			GlobalTransactionIdContexts.bind(globalTransactionIdWithAddressAndPort);
		} else
		{
			/**
			 * must participant
			 */
			if (globalTransactionIdWithAddressAndPort == null)
			{
				throw new ShouldNeverHappenException(globalTransactionRole + " is not in a global transaction " + "global_transaction_id_context.");
			}
			log.info(globalTransactionRole + " is already in global transaction " + globalTransactionIdWithAddressAndPort);
		}

	}

	/**
	 * 必须全局事务id存在
	 */
	private void check()
	{
		if (globalTransactionIdWithAddressAndPort == null)
		{
			throw new ShouldNeverHappenException();
		}

	}

	@Override
	public void commit() throws GlobalTransactionException
	{
		check();
		GlobalTransactionIdContexts.unbind();
		if (globalTransactionRole == GlobalTransactionRole.participant)
		{
			/**
			 * participant has no responsibility of committing
			 */
			return;
		}
		globalTransactionStatus = globalTransactionManager.globalTransactionCommit(globalTransactionIdWithAddressAndPort);

	}

	@Override
	public void rollback() throws GlobalTransactionException
	{
		check();
		GlobalTransactionIdContexts.unbind();
		if (globalTransactionRole == GlobalTransactionRole.participant)
		{
			/**
			 * participant has no responsibility of rollbacking
			 */
			return;
		}
		globalTransactionStatus = globalTransactionManager.globalTransactionRollback(globalTransactionIdWithAddressAndPort);

	}

	@Override
	public GlobalTransactionStatus getGlobalTransactionStatus() throws GlobalTransactionException
	{
		check();
		globalTransactionStatus = globalTransactionManager.getGlobalTransactionStatus(globalTransactionIdWithAddressAndPort);
		return globalTransactionStatus;
	}

}
